#include "jarvis.h"
//#define BUFFSIZE 1000000

Jarvis::Jarvis()
{
    this->edgePoints = new struct Point[1];
}

Jarvis::~Jarvis()
{
    clear();
}

void Jarvis::setPoints(Point *_points, int _pointsCount)
{
    this->points = _points;
    this->pointCount = _pointsCount;
}

Point *Jarvis::getPoints()
{
    return this->points;
}

int Jarvis::getPointsCount()
{
    return this->pointCount;
}

struct Point* Jarvis::getEdgesPoints()
{
    return this->edgePoints;
}

int Jarvis::getEdgesPoitnsCount()
{
    return this->edgePointsCount;
}

// wyznaczenie otoczki wyukłej algorytm Jarvisa
int Jarvis::process()
{
    if (this->pointCount < 1)
        return 0;
    // punkt startowy
    struct Point start = this->points[0];
	float startx,starty;//do przesylania

    for (int i=0+mynum; i<this->pointCount; i+=nproc)
    {
        if (this->points[i].y < start.y || (this->points[i].y == start.y && this->points[i].x < start.x))
            start = this->points[i];
    }
	struct Point startRcv;
	int source;
	if (mynum==0)
	{
		for (int i=1; i<nproc; i++) 
		{
			source = i;
			// oddzielnie odbieramy x oraz y
			info=MPI_Recv(&startx, 1, MPI_FLOAT, source, 2, MPI_COMM_WORLD, &status);
			info=MPI_Recv(&starty, 1, MPI_FLOAT, source, 2, MPI_COMM_WORLD, &status);
			if(starty < start.y || (start.y == starty && startx < start.x))
			{
					start.x = startx;
					start.y=starty;
			}
		}
	}
	else
	{
		// oddzielnie wysylamy x i y
		info = MPI_Send(&(start.x), 1, MPI_FLOAT, 0, 2, MPI_COMM_WORLD);
		info = MPI_Send(&(start.y), 1, MPI_FLOAT, 0, 2, MPI_COMM_WORLD);
	}
	//synchronizacja
	MPI_Barrier(MPI_COMM_WORLD);
	
	// rozeslanie do wszystkich punktu startowego
	if(mynum!=0)
	{
		info=MPI_Recv(&startx, 1, MPI_FLOAT, 0, 2, MPI_COMM_WORLD, &status);
		start.x=startx;
		info=MPI_Recv(&starty, 1, MPI_FLOAT, 0, 2, MPI_COMM_WORLD, &status);
		start.y=starty;
	}
	else
	{
		for(int i=1;i<nproc;i++)
		{
			info=MPI_Send(&(start.x), 1, MPI_INT, i, 2, MPI_COMM_WORLD);
			info=MPI_Send(&(start.y), 1, MPI_INT, i, 2, MPI_COMM_WORLD);
		}
	}

    this->edgePointsCount = 1;
    this->edgePoints = new struct Point[this->pointCount + 1];  // miejsce na punkty krawędzi
    this->edgePoints[0] = start;
    float maxAngle = 0.0;
    struct Point prevPoint = Point(start.x - 10, start.y);
    int edgePoint = 0;

	// obliczanie otoczki
    do
    {
        maxAngle = 0.0;
        for (int i=0+mynum; i<this->pointCount; i+=nproc)
        {
			if ((this->points[i].x != start.x || this->points[i].y != start.y)
				&& (this->points[i].x != prevPoint.x || this->points[i].y != prevPoint.y))
			{
				float angle = this->angle(prevPoint, start, this->points[i]);
				if (maxAngle < angle)
				{
					maxAngle = angle;       // największy kąt
					edgePoint = i;          // index punktu w tablicy
				}
			}
		}
		float retAngle;
		int retEdge;
		
		// zbieranie wyliczen przez proces 0
		if (mynum==0)
		{
			for (int i=1; i<nproc; i++) 
			{
				source = i;
				info=MPI_Recv(&retAngle, 1, MPI_FLOAT, source, 2, MPI_COMM_WORLD, &status);
				info=MPI_Recv(&retEdge, 1, MPI_INT, source, 2, MPI_COMM_WORLD, &status);
				if (maxAngle < retAngle)
				{
					maxAngle = retAngle;
					edgePoint = retEdge;
				}
			}
		}
		else
		{
			info = MPI_Send(&maxAngle, 1, MPI_FLOAT, 0, 2, MPI_COMM_WORLD);
			info = MPI_Send(&edgePoint, 1, MPI_INT, 0, 2, MPI_COMM_WORLD);
		}
		
		// synchronizacja
		MPI_Barrier(MPI_COMM_WORLD);

		int edgePointRcv;
		int flag=0;
		// rozeslanie do wszystkich nastepnego punktu (edgePoint)
		if(mynum!=0)
		{
			info=MPI_Recv(&edgePointRcv, 1, MPI_INT, 0, 2, MPI_COMM_WORLD, &status);
			edgePoint=edgePointRcv;
		}
		else
		{
			for(int i=1;i<nproc;i++)
			{
				info=MPI_Send(&edgePoint, 1, MPI_INT, i, 2, MPI_COMM_WORLD);
			}
		}
		
		//wyliczenie nowego punktu startowego
		prevPoint = start;
		start = this->points[edgePoint];
		this->edgePoints[this->edgePointsCount] = start;
		this->edgePointsCount = this->edgePointsCount + 1;
		if (edgePointsCount > this->pointCount)
			break;
	}
    while (start.x != this->edgePoints[0].x || start.y != this->edgePoints[0].y);
	return 0;
}

// zwraca kąt pomiędzy trzema punktami. p2 - środkowy
float Jarvis::angle(struct Point p1, struct Point p2, struct Point p3)
{
    // przyjęcie punktu p2 jako początek układu
    p1.x -= p2.x;
    p1.y -= p2.y;
    p3.x -= p2.x;
    p3.y -= p2.y;
    float angle1 = atan2(p3.y, p3.x);
    float angle2 = atan2(p1.y, p1.x);
    if (angle1 < 0)
        angle1 = 2 * PI + angle1;
    if (angle2 < 0)
        angle2 = 2 * PI + angle2;
	float angle = abs(angle1 - angle2);
	if (angle > PI)
		angle = 2 * PI - angle;
    return angle;
}

void Jarvis::clear()
{
    if (this->edgePoints != nullptr)
        delete [] this->edgePoints;
    this->edgePointsCount = 0;
}

void Jarvis::loadEdgesPoints(int count, struct Point* points)
{
	this->pointCount = count;
}
